/* $RCSFile: HTMLText.cs $
 * Apache license v2 2011 by Christian Surieux
 * $Source: ?/ADNCRM.Core/Helpers/HTMLText.cs $
 * $log$
 * Revision 6 2010/08/06 19:56:28  christian.surieux
 *   Added Log to All cs files
 */

using System;
using System.Text;
using System.Text.RegularExpressions;

namespace AddonNice.Helpers
{
	/// <summary>
	/// Nice HTMLText helper. Contains both the HTML string
	/// and a tag-free version of the same string.
	/// </summary>
	public struct HTMLText
	{
		private string _InnerHTML;
		private string _InnerXHTML;   //Same as _InnerHTML but converted to XHTML
		private string _InnerText;    //Same as _InnerHTML but completely tag-free 
		
		/// <summary>
		/// HTMLText
		/// </summary>
		/// <param name="myText"></param>
		public HTMLText(string myText)
		{
			_InnerHTML = myText;
			_InnerText = string.Empty;
			_InnerXHTML = string.Empty;
		}
		
		/// <summary>
		/// InnerHTML
		/// </summary>
		public string InnerHTML
		{
			get
			{
				return _InnerHTML;
			}
			set
			{
				_InnerHTML = value;
			}
		}

		/// <summary>
		/// InnerXHTML
		/// </summary>
		public string InnerXHTML
		{
			get
			{
				_InnerXHTML = GetXHtml(_InnerHTML);
				return _InnerXHTML;
			}
		}
		
		/// <summary>
		/// InnerText
		/// Same as InnerHTML but completely tag-free 
		/// </summary>
		public string InnerText
		{
			get
			{
				_InnerText = CleanHTML(_InnerHTML);
				return _InnerText;
			}
		}
		
		/// <summary>
		/// Removes any HTML tags
		/// </summary>
		private string CleanHTML(string input)
		{
			string output;
			output  =   Regex.Replace(input,@"(\<[^\<]*\>)", string.Empty);
			output  =   output.Replace("<", "&lt;");
			output  =   output.Replace(">", "&gt;");
			return output;
		}

		/// <summary>
		/// Returns XHTML
		/// </summary>
		private string GetXHtml(string input)
		{
			if (input == string.Empty || input == null)
				return string.Empty;

			string output;
			string regex;
			
			// Open tags (no attributes)
			regex   =   "<[A-Za-z0-9:]*[>,\\s]";
			output  =   Regex.Replace(input, regex, new MatchEvaluator(HTMLText.MatchToLower));
			
			// Closed tags
			regex   =   "</[A-Za-z0-9]*>";
			output  =   Regex.Replace(output, regex, new MatchEvaluator(HTMLText.MatchToLower));

			// Open tags (with attributes)
			regex   =   "<[a-zA-Z]+[a-zA-Z0-9:]*(\\s+[a-zA-Z]+[a-zA-Z0-9:]*((=[^\\s\"'<>]+)|(=\"[^\"]*\")|(='[^']*')|()))*\\s*\\/?\\s*>";
			output  =   Regex.Replace(output, regex, new MatchEvaluator(HTMLText.ProcessAttribute));
			
			// HR
			output  =   output.Replace("<hr>", "<hr />");
			
			// BR
			output  =   output.Replace("<br>", "<br />");
			return output;
		}

		/// <summary>
		/// Transforms the match to lowercase
		/// </summary>
		/// <param name="m"></param>
		/// <returns></returns>
		static string MatchToLower(Match m)
		{
			return m.ToString().ToLower();
		}		

		/// <summary>
		/// Quote the result
		/// </summary>
		/// <param name="m"></param>
		/// <returns></returns>
		static string QuoteAttibute(Match m) 
		{
			string str  =   m.ToString().Remove(0, 1).Trim();
			return  "=\"" + str + "\" ";
		}

		/// <summary>
		/// Quote the result (end tag)
		/// </summary>
		/// <param name="m"></param>
		/// <returns></returns>
		static string QuoteAttibuteEnd(Match m) 
		{
			string str  =   m.ToString().Remove(0, 1);
			str         =   str.Remove(str.Length - 1, 1);
			return "=\"" + str + "\">";
		}

		static string ProcessAttribute(Match m) 
		{
			string output;
			string regex;

			//Attribute value (no quote) to Quoted Attribute Value
			output  =   "=([^\",^\\s,.]*)[\\s]";
			regex   =   Regex.Replace(m.ToString(), output, new MatchEvaluator(HTMLText.QuoteAttibute));

			//Attribute to lowercase
			output  =   "\\s[^=\"]*=";
			regex   =   Regex.Replace(regex, output, new MatchEvaluator(HTMLText.MatchToLower));

			//Attribute value (no quote) to Quoted Attribute Value (end of tag)
			output  =   "=([^\",^\\s,.]*)[>]";
			return Regex.Replace(regex, output, new MatchEvaluator(HTMLText.QuoteAttibuteEnd));
		}
		
		/// <summary>
		/// Gets an abstract HTML of maxLenght characters maximum
		/// </summary>
		public string GetAbstractHTML(int maxLenght)
		{
			if ( maxLenght >= InnerHTML.Length || InnerHTML ==	string.Empty)
			{
				return InnerHTML;
			}
			else
			{
				string abstr    =   InnerHTML.Substring(0, maxLenght);
				abstr           =   abstr.Substring(0, abstr.LastIndexOf(' ')) + "...";
				return abstr;
			}
		}

		/// <summary>
		/// Gets an abstract text (no HTML tags!) of maxLenght characters maximum
		/// </summary>
		public string GetAbstractText(int maxLenght)
		{
			string abstr    =   InnerText;
			if (maxLenght >= abstr.Length || abstr == string.Empty)
			{
				return InnerText;
			}
			else
			{
				abstr       =   abstr.Substring(0, maxLenght);
				int l       =   abstr.LastIndexOf(' ');
				l           =   (l > 0) ? l : abstr.Length;
				abstr       =   abstr.Substring(0, l) + "...";
				return abstr.Trim();
			}
		}

		/// <summary>
		/// Break the text in rows of rowLenght characters maximum
		/// using HTML content
		/// </summary>
		public string GetBreakedHTML(int rowLenght)
		{
			return(breakThis(this.InnerHTML, rowLenght));
		}
		
		/// <summary>
		/// Break the text in rows of rowLenght characters maximum,
		/// using text content, useful for emails
		/// </summary>
		public string GetBreakedText(int rowLenght)
		{
			return(breakThis(this.InnerText, rowLenght));
		}
		
		private string breakThis(string input, int rowLenght)
		{
			//Clean up input
			char[] RemoveChar   =   {' ','\n'};
			input               =   input.Trim(RemoveChar);

			StringBuilder s     =   new StringBuilder();

			string row;
			int index           =   0;
			int last;
			int len             =   input.Length;
			int count           =   0;

			while(index <= len)
			{
				if((index + rowLenght) < len)
					row         =   input.Substring(index, rowLenght);
				else
				{
					//Last row
					s.Append(input.Substring(index));
					s.Append(Environment.NewLine);
					break;
				}

				//Seach for end of line
				last            =   row.IndexOf('\n');
				if (last == 0)
				{
					row         =   Environment.NewLine;
				}
				else if (last > 0)
				{
					row         =   row.Substring(0, last);
				}
				else
				{
					last        =   row.LastIndexOf(' ');
					if (last > 0)
					{
						row     =   row.Substring(0, last) + Environment.NewLine;
					}
				}
				s.Append(row);
				index           +=  row.Length;

				count++;

				//Avoid loop
				if(row == string.Empty || index == len || count > len)
					break;
			}
			//Clean up output
			return s.ToString().Trim(RemoveChar);
		}

		/// <summary>
		/// Converts the struct to a string value
		/// </summary>
		static public implicit operator string(HTMLText value)
		{
			return (value.InnerHTML);
		}

        /// <summary>
        /// Converts the struct from a string value
        /// </summary>
		static public implicit operator HTMLText(string value)
		{
			HTMLText h = new HTMLText(value);
			return h;
		}
	}
}